package com.zareal.ymcc.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.zareal.domain.User;
import com.zareal.domain.UserAccount;
import com.zareal.domain.UserBaseInfo;
import com.zareal.ymcc.StrUtils;
import com.zareal.ymcc.constans.CommonConstants;
import com.zareal.ymcc.domain.Login;
import com.zareal.ymcc.dto.RedisRegisterDto;
import com.zareal.ymcc.dto.RegisterUserDto;
import com.zareal.ymcc.enums.GlobalErrorCode;
import com.zareal.ymcc.feign.LoginFeignClient;
import com.zareal.ymcc.mapper.UserMapper;
import com.zareal.ymcc.result.JSONResult;
import com.zareal.ymcc.service.IUserAccountService;
import com.zareal.ymcc.service.IUserBaseInfoService;
import com.zareal.ymcc.service.IUserService;
import com.zareal.ymcc.util.AssertUtil;
import com.zareal.ymcc.util.BitStatesUtils;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author Wilson
 * @since 2022-09-23
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private LoginFeignClient loginFeignClient;

    @Autowired
    private IUserAccountService userAccountService;

    @Autowired
    private IUserBaseInfoService userBaseInfoService;

    @Autowired
    private UserMapper userMapper;

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void register(RegisterUserDto registerUserDto) {
        // 1.参数校验
        String mobile = registerUserDto.getMobile();
        String password = registerUserDto.getPassword();
        String smsCode = registerUserDto.getSmsCode();
        Integer regChannel = registerUserDto.getRegChannel();

        // 2判断验证码是否正确,先获取key regist：phone
        String key = String.format(CommonConstants.Register_PHONE_PREFIX, mobile);
        // 2.1code是code+时间戳
        Object code = redisTemplate.opsForValue().get(key);
        // 2.2断言是不是为空
        AssertUtil.isNotNull(code, GlobalErrorCode.PHONECODE_IS_NULL);
        // 2.3将code强转成registerDto
        RedisRegisterDto redisRegisterDto = (RedisRegisterDto) code;
        // 2.4比较传过来的smscode
        AssertUtil.isEquals(smsCode, redisRegisterDto.getCode(), GlobalErrorCode.PHONE_IS_ERROR);

        // 3.1判断是手机号是否注册过
        User user = selectByPhone(mobile);
        // 3.2断言user注册过没
        AssertUtil.isNull(user, GlobalErrorCode.USER_IS_EXISTED);

        /*
            4 用户不存在，则保存用户信息调用openfeign接口,这里保存login信息，User表中存在login_id
            jsonResult中有两个参数，一个success，另一个data
         */
        JSONResult jsonResult = saveLogin(registerUserDto);
        // 4.1 断言jsonResult是否成功，有可能熔断了
        AssertUtil.isTrue(jsonResult.isSuccess(), GlobalErrorCode.SERVICE_ERROR);
        // 4.2 判断login_id有没有
        AssertUtil.isNotNull(jsonResult.getData(), GlobalErrorCode.USER_LOGIN_ERROR);
        Long loginId = Long.valueOf(jsonResult.getData().toString());
        // 5.保存t_user、t_user_account、t_user_base_info
        User saveUser = saveUser(loginId, registerUserDto);
        iniUserAccount(saveUser, registerUserDto);
        iniUserBaseInfo(saveUser, registerUserDto);
        // 生成不重复的邀请码
        String inviteCode = findByInviteCode();
        // 保存到关联表中
        userMapper.saveInviteCodeById(saveUser.getId(), inviteCode);

    }

    @Override
    public String getInviteCode(Long id) {
        String code = userMapper.findCodeById(id);
        return code;
    }

    private String findByInviteCode() {
        while (true) {
            // 注册成功生成生成随机邀请码
            String inviteCode = StrUtils.getComplexRandomString(4);
            // 查询是否存在，存在就继续,没找到就继续创建
            Object o = userMapper.findByInviteCode(inviteCode);
            // o对象不存在的时候 证明不重复 则可以跳出循环返回值
            if (o ==null){
                return inviteCode;
            }
        }
    }

    private void iniUserBaseInfo(User saveUser, RegisterUserDto registerUserDto) {
        UserBaseInfo userBaseInfo = new UserBaseInfo();
        userBaseInfo.setCreateTime(new Date().getTime());
        userBaseInfo.setId(saveUser.getId());
        userBaseInfo.setRegChannel(registerUserDto.getRegChannel());
        // 不在一个类所以要注入他们的service
        userBaseInfoService.insert(userBaseInfo);
    }

    private void iniUserAccount(User saveUser, RegisterUserDto registerUserDto) {
        UserAccount userAccount = new UserAccount();
        userAccount.setCreateTime(new Date().getTime());
        userAccount.setId(saveUser.getId());
        userAccount.setPassword(registerUserDto.getPassword());
        userAccountService.insert(userAccount);
    }

    private User saveUser(Long loginId, RegisterUserDto registerUserDto) {
        User user = new User();
        user.setNickName(registerUserDto.getMobile());
        user.setCreateTime(new Date().getTime());
        user.setLoginId(loginId);
        user.setPhone(registerUserDto.getMobile());

        // 保存bit_state
        long bitState = BitStatesUtils.addState((BitStatesUtils.addState
                (BitStatesUtils.OP_REGISTED, BitStatesUtils.OP_ACTIVED)), BitStatesUtils.OP_AUTHED_PHONE);
        user.setBitState(bitState);
        insert(user);
        return user;
    }

    private JSONResult saveLogin(RegisterUserDto registerUserDto) {
        Login login = new Login();
        // 设置参数
        login.setUsername(registerUserDto.getMobile());
        login.setPassword(registerUserDto.getPassword());
        login.setEnabled(Login.ENABLED);
        login.setType(Login.FORE_TYPE);
        // 返回的时候自带login_id
        return loginFeignClient.saveOrUpdate(login);

    }

    private User selectByPhone(String mobile) {
        Wrapper<User> wrapper = new EntityWrapper<>();
        wrapper.eq("phone", mobile);
        return selectOne(wrapper);
    }
}
